﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using PaperRenderingInterfaces;
using HtmlAgilityPack;
using DeepZoomPivotConstructor.PivotLib;
using PaperPluginUtilities;
using DeepZoomPivotConstructor.VisualLib;
using System.IO;
using RenderEngineInterfaces;

namespace HTMLTablePageScanner
{
    /// <summary>
    /// The top level rendering object that looks for tables in html pages and then attempts to extract everything
    /// from those tables.
    /// 
    /// Works on the principle that everyone puts all the info in a single row. So all interesting figures in that row are assumed to
    /// be related by the text/crap that is found.
    /// 
    /// Designed to work with a single layer of tables only.
    /// 
    /// If only we had metadata!!!
    /// </summary>
    [Export(typeof(IPaperRender))]
    [PaperRender("HTMLSimpleTables")]
    public class WebPageScanner : IPaperRender
    {
        /// <summary>
        /// Grabs the data from the web for us!
        /// </summary>
        [Import]
        IWebAccess WebGetter;

        /// <summary>
        /// Used to render to a visual the things we actuall uncover!
        /// </summary>
        [Import]
        IRenderEngine MyRenderer;

        /// <summary>
        /// Extract the data from the html table page
        /// </summary>
        /// <param name="renderAddress"></param>
        /// <returns></returns>
        public IEnumerable<PivotImage> Render(string renderAddress)
        {
            ///
            /// First, we need to get the data from URL
            /// 

            Uri u = new Uri(renderAddress);
            string htmlData = WebGetter.GetUriAsString(u);

            ///
            /// Load it into our html parsing utilities
            /// 

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(htmlData);

            ///
            /// Extract the web page title
            /// 

            string title = doc.DocumentNode.ExtractHTMLTitle();
            if (title == null)
            {
                title = "<no title>";
            }

            ///
            /// Now, loop over all tables that we can find in there
            /// 

            var images = new EnumerableWrapper<HtmlNode>(doc.DocumentNode.SelectNodes("//table"))
                    .SelectMany(tn => FindImagesInTable(tn, u))
                    .Apply(i => i.Add("More Information", "Web Page", u))
                    .Apply(i => i.Add("Note Title", title))
                    .AddMissingDate(u, WebGetter);

            return images;
        }

        /// <summary>
        /// Find the images in a particular table
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        private IEnumerable<PivotImage> FindImagesInTable(HtmlNode n, Uri baseUri)
        {
            string fixedHtml = FixUpTableHtml(n.InnerHtml);
            n.InnerHtml = fixedHtml;
            return n.ScanSubNodes(h => h.Name == "tr", h => h.Name == "table").SelectMany(hn => FindImagesInTableRow(hn, baseUri));
        }

        /// <summary>
        /// Try to repair table html. Specifically, look for and fix up the following errors:
        ///   - <tr> with no closing </tr>
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private string FixUpTableHtml(string tableInnerHtml)
        {
            string[] allTRs = tableInnerHtml.Split(new string[] { "<tr" }, StringSplitOptions.RemoveEmptyEntries);
            string result = "";
            foreach (var item in allTRs)
            {
                if (item.StartsWith(">"))
                {
                    result += "<tr" + item;
                }
                if (!item.Contains("</tr"))
                {
                    result += "</tr>";
                }
            }
            return result;
        }

        /// <summary>
        /// Searching for the info in a particular table row.
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private IEnumerable<PivotImage> FindImagesInTableRow(HtmlNode node, Uri baseUri)
        {
            string description = "";
            ///
            /// Look for text... BUild it all up from this set of table legs!
            /// 

            node.ScanForText(t => description = t, new string[0]).Execute(h => { });

            ///
            /// Now, look through for all the links
            /// 

            List<ImageUrlParsing.LinkInfo> links = new List<ImageUrlParsing.LinkInfo>();
            node.ScanSubNodes("td").Execute(n =>
                {
                    ///
                    /// Look for links
                    /// 

                    n.ScanSubNodes("a").Execute(linkNode =>
                        {
                            ImageUrlParsing.LinkInfo info;
                            var hrefAttr = linkNode.Attributes["href"];
                            if (hrefAttr != null)
                            {
                                info.url = hrefAttr.Value;
                                info.urlName = linkNode.InnerText.CleanHTMLText();
                                if (info.urlName == null || info.urlName.Length == 0)
                                {
                                    info.urlName = Path.GetFileNameWithoutExtension(ImageUrlParsing.GetPictureFileName(info.url));
                                }
                                else
                                {
                                    info.urlName = CleanFilenameOfKnownExtensions(info.urlName);
                                }
                                links.Add(info);
                            }
                        });
                });

            ///
            /// Some of the links may be to more than a single picutre, but with multiple
            /// formats
            /// 

            var groupedImages = from l in links
                                where ImageUrlParsing.IsPictureType(l.url)
                                group l by ImageUrlParsing.GetPictureFileName(l.url);

            ///
            /// Next look through all the links (if we found them) and see if we can't create a pivot
            /// image associated with them - eps, gif, etc.
            /// 

            foreach (var pics in groupedImages)
            {
                var info = ImageUrlParsing.GetBestImage(pics);
                Uri infoUri = new Uri(baseUri, info.url);
                Visual v = MyRenderer.Render(infoUri, forceAsComposableUnit: true);
                PivotImage p = new PivotImage(v);
                p.Name = General.DecideOnImageName(new string[] {info.urlName, description}, infoUri);
                p.Description = description.CleanHTMLText();

                pics.Execute(i => p.Add("Graphics Files", ImageUrlParsing.GetNormalizedFileExtension(i.url), new Uri(baseUri, i.url)));
                p.Add("Caption", description);

                p.SetDateFromUrl(infoUri, WebGetter);

                yield return p;
            }
        }

        private string[] _knownExtensions = { ".gif", ".jpg", ".jpeg", ".pdf", ".eps", ".ps" };

        /// <summary>
        /// Given a filename, if it ends in a known extension, then remove it!
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private string CleanFilenameOfKnownExtensions(string fName)
        {
            string fNameLower = fName.ToLower();
            var matched = _knownExtensions.Where(ext => fNameLower.EndsWith(ext)).FirstOrDefault();
            if (matched == null)
            {
                return fName;
            }
            else
            {
                return fName.Substring(0, fName.Length - matched.Length);
            }
        }
    }
}
